package ca.usherbrooke.domus.sipnmove.call.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import ca.usherbrooke.domus.sipnmove.call.Call;

/**
 * Socket call
 * 
 * Communicate messages via socket
 */
public class SocketCall implements Call {

	/**
	 * Socket call listeners
	 */
	private Set<SocketCallListener> socketCallListeners;

	/**
	 * Socket
	 */
	private Socket socket;

	/**
	 * Socket output buffer
	 */
	private PrintWriter outputBuffer;

	/**
	 * Socket input buffer
	 */
	private BufferedReader inputBuffer;

	/**
	 * Server thread for socket messages handling
	 */
	private Thread serverThread;

	/**
	 * Define if this call should act as a Socket Server
	 */
	private boolean isServer;

	/**
	 * Default constructor
	 */
	public SocketCall(boolean isServer) {
		this.isServer = isServer;
		socketCallListeners = new HashSet<SocketCallListener>();
	}

	/**
	 * Add a SIP call listener
	 */
	public void addSocketCallListener(SocketCallListener socketCallListener) {
		socketCallListeners.add(socketCallListener);
	}

	/**
	 * Send a message via the socket
	 * 
	 * TODO : This method should be placed in an implemented interface like
	 * MessageCall or whatever
	 * 
	 * @param message
	 */
	public void sendMessage(String message) {
		if (socket == null)
			System.err.println("Socket has not been initialized");

		outputBuffer.println(message);

		notifyMessageSent(message);
	}

	@Override
	public void accept() {
		// TODO Auto-generated method stub

	}

	@Override
	public void initiate(final Map<String, Object> callInformation) {
		final Integer port = (Integer) callInformation.get("port");

		serverThread = new Thread() {
			public void run() {
				String message;
				try {

					if (isServer) {
						ServerSocket socketServer = new ServerSocket(port);
						socket = socketServer.accept();
					} else {
						String ip = (String) callInformation.get("ip");
						socket = new Socket(ip, port);
					}

					outputBuffer = new PrintWriter(socket.getOutputStream(), true);
					inputBuffer = new BufferedReader(new InputStreamReader(socket.getInputStream()));

					// Loop until thread is deleted
					while (Thread.currentThread() == serverThread) {
						message = inputBuffer.readLine();

						if (message != null)
							notifyMessageReceived(message);
					}

				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		};

		serverThread.start();
	}

	@Override
	public void terminate() {
		serverThread = null;
	}

	@Override
	public void update(Map<String, Object> callInformation) {

	}

	/**
	 * Notify listeners that a message has been sent
	 */
	private void notifyMessageSent(String message) {
		for (SocketCallListener socketCallListener : socketCallListeners) {
			socketCallListener.processSentMessage(message);
		}
	}

	/**
	 * Notify listeners that a message has been received
	 */
	private void notifyMessageReceived(String message) {
		for (SocketCallListener socketCallListener : socketCallListeners) {
			socketCallListener.processReceivedMessage(message);
		}
	}
}
